package com.ruoyi.iot.service.impl;

import com.ruoyi.iot.domain.entity.DeviceAlarmRecord;
import com.ruoyi.iot.domain.entity.DeviceAlarmWindow;
import com.ruoyi.iot.domain.entity.DeviceElectricalWindow;
import com.ruoyi.iot.domain.entity.DeviceFaultWindow;
import com.ruoyi.iot.domain.entity.DeviceTemperatureWindow;
import com.ruoyi.iot.domain.entity.DeviceThresholdConfig;
import com.ruoyi.iot.domain.enums.DeviceTimeScope;
import com.ruoyi.iot.mapper.DeviceAlarmRecordMapper;
import com.ruoyi.iot.mapper.DeviceAlarmWindowMapper;
import com.ruoyi.iot.mapper.DeviceElectricalWindowMapper;
import com.ruoyi.iot.mapper.DeviceFaultWindowMapper;
import com.ruoyi.iot.mapper.DeviceTemperatureWindowMapper;
import com.ruoyi.iot.mapper.DeviceThresholdConfigMapper;
import com.ruoyi.iot.service.DeviceAlarmRecordService;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.ruoyi.iot.service.HostDeviceMetricsService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

/**
 * 告警记录服务实现
 */
@Service
public class DeviceAlarmRecordServiceImpl implements DeviceAlarmRecordService {

    private static final String SOURCE_ELECTRIC = "electric";
    private static final String SOURCE_ALARM = "alarm";
    private static final String SOURCE_FAULT = "fault";
    private static final String SOURCE_TEMPERATURE = "temperature";

    /**
     * 预先构建映射，便于通过指标编码快速定位窗口字段
     */
    private static final Map<String, Field> ELECTRICAL_FIELD_CACHE = buildFieldCache(DeviceElectricalWindow.class);
    private static final Map<String, Field> ALARM_FIELD_CACHE = buildFieldCache(DeviceAlarmWindow.class);
    private static final Map<String, Field> FAULT_FIELD_CACHE = buildFieldCache(DeviceFaultWindow.class);
    private static final Map<String, Field> TEMPERATURE_FIELD_CACHE = buildFieldCache(DeviceTemperatureWindow.class);

    private final DeviceElectricalWindowMapper electricalWindowMapper;
    private final DeviceAlarmWindowMapper alarmWindowMapper;
    private final DeviceFaultWindowMapper faultWindowMapper;
    private final DeviceTemperatureWindowMapper temperatureWindowMapper;
    private final DeviceThresholdConfigMapper thresholdConfigMapper;
    private final DeviceAlarmRecordMapper alarmRecordMapper;
    private final HostDeviceMetricsService hostDeviceMetricsService;

    public DeviceAlarmRecordServiceImpl(DeviceElectricalWindowMapper electricalWindowMapper,
                                        DeviceAlarmWindowMapper alarmWindowMapper,
                                        DeviceFaultWindowMapper faultWindowMapper,
                                        DeviceTemperatureWindowMapper temperatureWindowMapper,
                                        DeviceThresholdConfigMapper thresholdConfigMapper,
                                        DeviceAlarmRecordMapper alarmRecordMapper,
                                        HostDeviceMetricsService hostDeviceMetricsService) {
        this.electricalWindowMapper = electricalWindowMapper;
        this.alarmWindowMapper = alarmWindowMapper;
        this.faultWindowMapper = faultWindowMapper;
        this.temperatureWindowMapper = temperatureWindowMapper;
        this.thresholdConfigMapper = thresholdConfigMapper;
        this.alarmRecordMapper = alarmRecordMapper;
        this.hostDeviceMetricsService = hostDeviceMetricsService;
    }

    /**
     * 根据设备最新窗口数据判定告警并落库
     */
    @Override
    public void syncLatestRecords(String deviceId) {
        if (StringUtils.isBlank(deviceId)) {
            return;
        }
        DeviceElectricalWindow electrical = electricalWindowMapper.selectLatestWindow(deviceId, 10);
        DeviceAlarmWindow alarm = alarmWindowMapper.selectLatestWindow(deviceId, 5);
        DeviceFaultWindow fault = faultWindowMapper.selectLatestWindow(deviceId, 30);
        DeviceTemperatureWindow temperature = temperatureWindowMapper.selectLatestWindow(deviceId, 10);

        if(null == temperature){
            temperature = temperatureWindowMapper.selectLatestWindow(hostDeviceMetricsService.findDeviceIdByDeviceKgId(deviceId), 10);
        }

        if (electrical == null && alarm == null && fault == null && temperature == null) {
            return;
        }
        List<DeviceThresholdConfig> configs = loadAlarmThresholds(deviceId);
        if (CollectionUtils.isEmpty(configs)) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        List<DeviceAlarmRecord> records = new ArrayList<>();
        for (DeviceThresholdConfig config : configs) {
            //获取指标数值
            MetricValue metricValue = resolveMetricValue(config.getMetricCode(), electrical, alarm, fault, temperature);
            if (metricValue == null || metricValue.value == null) {
                continue;
            }
            BigDecimal threshold = config.getAlarmValue();//告警阈值
            if (threshold == null || !match(metricValue.value, threshold, config.getComparator())) {
                continue;
            }
            DeviceAlarmRecord record = new DeviceAlarmRecord();
            record.setDeviceId(deviceId);
            record.setMetricCode(config.getMetricCode());
            record.setMetricValue(metricValue.value);
            record.setThresholdValue(threshold);
            record.setComparator(config.getComparator());
            record.setAlarmLevel(config.getAlarmLevel());
            record.setSourceType(metricValue.sourceType);
            record.setWindowEnd(metricValue.windowEnd);
            record.setCreatedAt(now);
            record.setUpdatedAt(now);
            records.add(record);
        }
        if (CollectionUtils.isNotEmpty(records)) {
            alarmRecordMapper.insertBatch(records);
        }
    }

    @Override
    public Map<String, Long> countAlarmLevels(String deviceId, DeviceTimeScope scope) {
        if (StringUtils.isBlank(deviceId)) {
            return buildEmptyCount();
        }
        DeviceTimeScope effectiveScope = scope != null ? scope : DeviceTimeScope.MONTH;
        ZoneId zone = ZoneId.systemDefault();
        ZonedDateTime start = effectiveScope.rangeStart();
        if (!start.getZone().equals(zone)) {
            start = start.withZoneSameInstant(zone);
        }
        ZonedDateTime end;
        switch (effectiveScope) {
            case TODAY:
                end = start.plusDays(1);
                break;
            case QUARTER:
                end = start.plusMonths(3);
                break;
            case YEAR:
                end = start.plusYears(1);
                break;
            case MONTH:
            default:
                end = start.plusMonths(1);
                break;
        }
        ZonedDateTime now = ZonedDateTime.now(zone);
        if (end.isAfter(now)) {
            end = now;
        }
        if (!end.isAfter(start)) {
            end = start.plusSeconds(1);
        }
        Map<String, Object> row = alarmRecordMapper.countByLevel(
            deviceId,
            start.toLocalDateTime(),
            end.toLocalDateTime());
        if (row == null) {
            return buildEmptyCount();
        }
        Map<String, Long> result = buildEmptyCount();
        long electric = MapUtils.getLongValue(row, "electricCount", 0L);
        long alarm = MapUtils.getLongValue(row, "alarmCount", 0L);
        long fault = MapUtils.getLongValue(row, "faultCount", 0L);
        long temperature = MapUtils.getLongValue(row, "temperatureCount", 0L);
        long total = MapUtils.getLongValue(row, "totalCount", 0L);
        result.put("electric", electric);
        result.put("alarm", alarm);
        result.put("fault", fault);
        result.put("temperature", temperature);
        result.put("total", total);
        return result;
    }

    private Map<String, Long> buildEmptyCount() {
        Map<String, Long> result = new LinkedHashMap<>(5);
        result.put("electric", 0L);
        result.put("alarm", 0L);
        result.put("fault", 0L);
        result.put("temperature", 0L);
        result.put("total", 0L);
        return result;
    }

    /**
     * 加载设备级和全局级别的告警阈值，后者作为默认配置
     */
    private List<DeviceThresholdConfig> loadAlarmThresholds(String deviceId) {
        Map<String, DeviceThresholdConfig> merged = new LinkedHashMap<>();
        for (DeviceThresholdConfig config : thresholdConfigMapper.selectByDeviceId(null)) {
            if (isAlarmLevel(config)) {
                merged.put(buildKey(config), config);
            }
        }
        for (DeviceThresholdConfig config : thresholdConfigMapper.selectByDeviceId(deviceId)) {
            if (isAlarmLevel(config)) {
                merged.put(buildKey(config), config);
            }
        }
        return new ArrayList<>(merged.values());
    }

    private boolean isAlarmLevel(DeviceThresholdConfig config) {
        return config != null
            && StringUtils.equalsAnyIgnoreCase(config.getAlarmLevel(), "level1", "level2", "level3");
    }

    private String buildKey(DeviceThresholdConfig config) {
        return String.format(Locale.ROOT, "%s#%s",
            normalize(config.getMetricCode()),
            StringUtils.defaultString(config.getAlarmLevel()));
    }

    /**
     * 根据配置指标解析窗口中的数值
     */
    private MetricValue resolveMetricValue(String metricCode,
                                           DeviceElectricalWindow electrical,
                                           DeviceAlarmWindow alarm,
                                           DeviceFaultWindow fault,
                                           DeviceTemperatureWindow temperature) {
        if (StringUtils.isBlank(metricCode)) {
            return null;
        }
        String normalized = normalize(metricCode);
        Field field = ELECTRICAL_FIELD_CACHE.get(normalized);
        if (field != null && electrical != null) {
            BigDecimal value = readNumeric(field, electrical);
            if (value != null) {
                return new MetricValue(value, SOURCE_ELECTRIC, electrical.getWindowEnd());
            }
        }
        field = ALARM_FIELD_CACHE.get(normalized);
        if (field != null && alarm != null) {
            BigDecimal value = readNumeric(field, alarm);
            if (value != null) {
                return new MetricValue(value, SOURCE_ALARM, alarm.getWindowEnd());
            }
        }
        field = FAULT_FIELD_CACHE.get(normalized);
        if (field != null && fault != null) {
            BigDecimal value = readNumeric(field, fault);
            if (value != null) {
                return new MetricValue(value, SOURCE_FAULT, fault.getWindowEnd());
            }
        }
        field = TEMPERATURE_FIELD_CACHE.get(normalized);
        if (field != null && temperature != null) {
            BigDecimal value = readNumeric(field, temperature);
            if (value != null) {
                return new MetricValue(value, SOURCE_TEMPERATURE, temperature.getWindowEnd());
            }
        }
        return null;
    }

    /**
     * 统一的数值判断逻辑
     */
    private boolean match(BigDecimal metricValue, BigDecimal threshold, String comparator) {
        if (metricValue == null || threshold == null || StringUtils.isBlank(comparator)) {
            return false;
        }
        int cmp = metricValue.compareTo(threshold);
        switch (comparator.trim()) {
            case ">":
                return cmp > 0;
            case ">=":
                return cmp >= 0;
            case "<":
                return cmp < 0;
            case "<=":
                return cmp <= 0;
            case "=":
            case "==":
                return cmp == 0;
            default:
                return false;
        }
    }

    /**
     * 通过反射读取窗口字段，支持 Integer、Double、BigDecimal 等数值类型
     */
    private BigDecimal readNumeric(Field field, Object target) {
        try {
            Object value = field.get(target);
            if (value == null) {
                return null;
            }
            if (value instanceof BigDecimal) {
                return (BigDecimal) value;
            }
            if (value instanceof Number) {
                return BigDecimal.valueOf(((Number) value).doubleValue());
            }
            return new BigDecimal(value.toString());
        } catch (IllegalAccessException | NumberFormatException ex) {
            return null;
        }
    }

    private static Map<String, Field> buildFieldCache(Class<?> type) {
        Map<String, Field> cache = new HashMap<>();
        for (Field field : type.getDeclaredFields()) {
            if (!Number.class.isAssignableFrom(field.getType())) {
                continue;
            }
            field.setAccessible(true);
            cache.put(normalize(field.getName()), field);
        }
        return cache;
    }

    private static String normalize(String value) {
        return value == null ? "" : value.toLowerCase(Locale.ROOT);
    }

    private static class MetricValue {
        private final BigDecimal value;
        private final String sourceType;
        private final LocalDateTime windowEnd;

        MetricValue(BigDecimal value, String sourceType, LocalDateTime windowEnd) {
            this.value = value;
            this.sourceType = sourceType;
            this.windowEnd = windowEnd;
        }
    }
}
